color BORDERCOLOR  = color(255);
color FILLCOLOR    = color(24,50,90);
color TEXTCOLOR    = color(255);
color BUTTONSTROKE = color(255); 
color BUTTONFILL   = color(114,159,207);
color BUTTONOVER   = color(52,101,164);
color BUTTONDOWN   = color(37,74,135);
//the idea with layers is that there can be multiple layers of focus, very similar to the idea
//of containers... the bottom layer is where the user will be most often
//each layer above that is considered to be a "popup" on top of that layer
//all normal events (mouse,keyboard,draw cycles) happen only in the "active" layer
//layers should be able to pass data back and forth (like filenames, boolean values)
//hopefully all this will allow neat graying effects for inactive layers

//some parameters...
//RESIZEENABLED...should this layer be resizable?

class Layer
{
  boolean RESIZEENABLED = false;
  int MINHEIGHT = 10;
  int MINWIDTH  = 10;
  int MINX = 0;
  int MINY = 0;
  boolean DONE = false;  
  
  PGraphics pg;
  int x, y, w, h, parent;
  boolean dragActive = false;
  boolean[] resizeActive = {false,false,false,false}; //left,up,right,down
  
  Layer(int _x, int _y, int _w, int _h, int p)
  {
    x = _x; w = _w;
    y = _y; h = _h;
    parent = p;
    pg = createGraphics(w,h,JAVA2D);
    pg.beginDraw();
    pg.background(0);
    pg.endDraw();
    setCursor(new Cursor(0));
  }
  
  boolean mDraw()
  {
    //pg = createGraphics(w,h,JAVA2D);
    pg.beginDraw();
    pg.background(BORDERCOLOR);
    pg.fill(FILLCOLOR);
    pg.noStroke();
    pg.rect(3,3,pg.width-6,pg.height-6);
    pg.endDraw();
    pg.modified = true;
    doCursor();
    return DONE;
  }
  
  void mMessage(Message m)
  {
    if(m.type == 1) println("Boolean message: " + m.data);
    else if(m.type == 2) println("String message: " + m.data);
    else println("Generic message receied.");
  }
  
  void mPress()
  {
    //this needs to be overridden so that it's not ALWAYS dragging
    dragActive = true;
    if(RESIZEENABLED) checkResize();
  }
  
  void checkResize()
  {
    //later it might be useful to have this return a boolean if any resizing is in play...
    //and allow the user to pass in a false for "just checking" or true for "go ahead and
    //set the values of resizeActive."  This would allow me to do a simple check during the
    //draw routine and change the cursor to a resize cursor when resizing is possible.
    Arrays.fill(resizeActive,false);
    if(mTest() == 1)
    {
      if(abs(mouseX-x) <= 5) resizeActive[0] = true;
      if(abs(mouseY-y) <= 5) resizeActive[1] = true;
      if(abs(mouseX-(x+w)) <= 5) resizeActive[2] = true;     
      if(abs(mouseY-(y+h)) <= 5) resizeActive[3] = true;
    }
    if(resizeActive[0] || resizeActive[1] || resizeActive[2] || resizeActive[3])
    {
      dragActive = false;
    }
  }
  
  void mRelease()
  {
    //this needs to be replicated if super.mRelease() isn't called
    dragActive = false;
    Arrays.fill(resizeActive,false);
    //resizeActive = {false,false,false,false};
  }
  
  
  void mDrag()
  {

    if(dragActive)
    {
      x += mouseX-pmouseX;
      y += mouseY-pmouseY;
    }
    
    if(resizeActive[0] && mouseX > 0)
    {
      x += mouseX-pmouseX;
      w -= mouseX-pmouseX;
    }
    if(resizeActive[1] && mouseY > 0)
    {
      y += mouseY-pmouseY;
      h -= mouseY-pmouseY;
    }  
    if(resizeActive[2] && mouseX < width)
    {
      w += mouseX-pmouseX;
    }    
    if(resizeActive[3] && mouseY < height)
    {
      h += mouseY-pmouseY;
    }
    w = constrain(w,MINWIDTH,width-MINX);
    h = constrain(h,MINHEIGHT,height-MINY);
    x = constrain(x,MINX,width-w);  //modified out to 40 for an icon tray!
    y = constrain(y,MINY,height-h);
    
    //some mouse weirdness can happen if the user drags the mouse offscreen and then back...but
    //this is much simpler than if I actually marked and saved a registration point on the layer
    //and tried to make that point move to the mouse's location...  some day.
  }
  
  
  void mKeyDown()
  {
  }
  
  void mKeyUp()
  {
  }
  
  void mAccept()
  {
  }
  
  void mCancel()
  {
  }
  
  void doCursor()
  {
    int test = mTest();
    if(test == 0) return;  //someone else has control!
    
    if(test == 1 && !dragActive && RESIZEENABLED)
    {
      //cardinals
      if(abs(mouseX-x) <= 5) setCursor(new Cursor(10));
      if(abs(mouseX-(x+w)) <= 5) setCursor(new Cursor(11));
      if(abs(mouseY-y) <= 5) setCursor(new Cursor(8));
      if(abs(mouseY-(y+h)) <= 5) setCursor(new Cursor(9));
      //diagonals
      if(abs(mouseX-x) <= 5 && abs(mouseY-y) <= 3) setCursor(new Cursor(6));
      if(abs(mouseX-x) <= 5 && abs(mouseY-(y+h)) <= 3) setCursor(new Cursor(4));
      if(abs(mouseX-(x+w)) <= 5 && abs(mouseY-y) <= 3) setCursor(new Cursor(7));
      if(abs(mouseX-(x+w)) <= 5 && abs(mouseY-(y+h)) <= 3) setCursor(new Cursor(5));
    }else if(dragActive){
      setCursor(new Cursor(13));
    }else{
      setCursor(new Cursor(0));
    }
  }
  
  //returns 0 for no collision
  //returns 1 for border collision (useful if resizing 2 adjacent layers)
  //returns 2 for inside collision
  int mTest()
  {
    if (mouseX > x+3 && mouseX < x+w-3 && mouseY > y+3 && mouseY < y+h-3) return 2;
    if (mouseX > x-3 && mouseX < x+w+3 && mouseY > y-3 && mouseY < y+h+3) return 1;    
    return 0;
  }
}

/****************
* PopConfirm is a popup dialogue for confirming actions (Yes || No)
****************/
class PopConfirm extends Layer
{
  String label;
  int target; //target command
  boolean DRAWNO = true;  //a PopConfirm without a "No" button is essentially a warning
  
  PopConfirm(int layer, int targ, String t, int popNum)
  {
    super(200 + 10*popNum, 100 + 10*popNum, 200,100,layer);
    RESIZEENABLED = true;
    MINHEIGHT = 100;
    MINWIDTH = 200;
    label = t;   
    target = targ;
  }
  
  boolean mDraw()
  {
    pg = createGraphics(w,h,JAVA2D);
    pg.beginDraw();
    pg.background(BORDERCOLOR);
    pg.fill(FILLCOLOR);
    pg.noStroke();
    pg.rect(3,3,pg.width-6,pg.height-6);
    pg.fill(TEXTCOLOR);
    pg.textFont(font,14);
    pg.text(label,13,30);
    pg.textFont(font,20);
    pg.strokeWeight(1);
    pg.stroke(BUTTONSTROKE);
    if(DRAWNO && mouseX > x + 13 && mouseX < x + 95 && mouseY > y + pg.height-57 && mouseY < y + pg.height-13)
    {
      if(mousePressed) pg.fill(BUTTONDOWN);
      else pg.fill(BUTTONOVER);
    }else{
      pg.fill(BUTTONFILL); 
    }
    if(DRAWNO) pg.rect(13,pg.height-57,82,44);
    
    if(mouseX > x + pg.width-95 && mouseX < x + pg.width-13 && mouseY > y + pg.height-57 && mouseY < y + pg.height-13)
    {
      if(mousePressed) pg.fill(BUTTONDOWN);
      else pg.fill(BUTTONOVER);
    }else{
      pg.fill(BUTTONFILL); 
    }
    pg.rect(pg.width-95,pg.height-57,82,44);    

    pg.fill(TEXTCOLOR);
    if(DRAWNO)
    { 
      pg.text("No",40,pg.height-28);
      pg.text("Yes",pg.width-72,pg.height-28);
    }else{
      pg.text("OK",pg.width-72,pg.height-28);
    }
    pg.endDraw();
    
    doCursor();
    
    return DONE;
  }
  
  void mRelease()
  {
    super.mRelease();
    if(DRAWNO && mouseX > x + 13 && mouseX < x + 95 && mouseY > y + pg.height-57 && mouseY < y + pg.height-13 && !dragActive)
    {    
      mCancel();
    }else if(mouseX > x + pg.width-95 && mouseX < x + pg.width-13 && mouseY > y + pg.height-57 && mouseY < y + pg.height-13 && !dragActive)
    {
      mAccept();
    }
  }
  
  void mPress()
  {
    if(DRAWNO && mouseX > x + 13 && mouseX < x + 13 + 82 && mouseY > y + 43 && mouseY < y + 43 + 44)
    {
      //do nothing...over the button
    }else if(mouseX > x + 105 && mouseX < x + pg.width-13 && mouseY > y + 43 && mouseY < y + 43 + 44){
      //do nothing...over the button
    }else if(mouseX > x && mouseX < x+pg.width && mouseY > y && mouseY < y+pg.height){
      dragActive = true;
    }
    if(RESIZEENABLED) checkResize();
  }
  
  void mKeyDown()
  {
    if(keyCode == ENTER) mAccept();    
  }
  
  //messaging needs to be done down here...
  void mAccept()
  {
    if(!DONE) messages.add(new Message(true,parent,target));
    DONE = true;
  }
  
  void mCancel()
  {
    if(!DONE) messages.add(new Message(false,parent,target));
    DONE = true;
  }
}

//this function creates the OK-only version of a PopConfirm
void popWarn(String s)
{
  PopConfirm woops = new PopConfirm(0,0,s,0);
  woops.DRAWNO = false;
  layers.add(woops);
  ACTIVELAYER++; 
}
void popWarn(String s,int w)
{
  PopConfirm woops = new PopConfirm(0,0,s,0);
  woops.w = w;
  woops.DRAWNO = false;
  layers.add(woops);
  ACTIVELAYER++;
}

/****************
* PopText is a class for getting text entry (as in a filename/path), with support for "Cancel"
****************/
class PopText extends Layer
{
  String textEntry = "";
  String label;
  int target;
  
  PopText(int layer, int targ, String t, int popNum)
  {
    super(200 + 10*popNum, 100 + 10*popNum, 200,100,layer);
    RESIZEENABLED = true;    
    MINHEIGHT = 100;
    MINWIDTH = 200;    
    label = t;
    target = targ;
  }
  
  boolean mDraw()
  {  
     pg = createGraphics(w,h,JAVA2D);
     pg.beginDraw();
     pg.background(BORDERCOLOR);
     pg.fill(FILLCOLOR);
     pg.noStroke();
     pg.rect(3,3,pg.width-6,pg.height-6);
     pg.strokeWeight(1);
     pg.stroke(BUTTONSTROKE);
     pg.fill(BUTTONDOWN);
     pg.rect(13,30,pg.width-26,20);

     if(mouseX > x+pg.width-53 && mouseX < x+pg.width-13 && mouseY > y+pg.height-38 && mouseY < y+pg.height-13)
     {
      if(mousePressed) pg.fill(BUTTONDOWN);
      else pg.fill(BUTTONOVER);
     }else{
       pg.fill(BUTTONFILL);
     }
     
     pg.rect(pg.width-53,pg.height-38,40,25);
     
     if(mouseX > x+pg.width-118 && mouseX < x+pg.width-58 && mouseY > y+pg.height-38 && mouseY < y+pg.height-13)
     {
      if(mousePressed) pg.fill(BUTTONDOWN);
      else pg.fill(BUTTONOVER);
     }else{
       pg.fill(BUTTONFILL);
     }     
     pg.rect(pg.width-118,pg.height-38,60,25);
  
     pg.fill(TEXTCOLOR);
     pg.textFont(font,16);
     pg.text(label,13,22);  
     pg.text(textEntry,15,45);   
     pg.text("Cancel",pg.width-112,pg.height-18);
     pg.text("OK",pg.width-42,pg.height-18);
     pg.endDraw();
     
     doCursor();
    
     return DONE;
  }
  
  void mKeyDown()
  {
    if(keyCode == ENTER) mAccept();    
    if(keyCode == BACKSPACE && textEntry.length() > 0) textEntry = textEntry.substring(0,textEntry.length()-1);    
  }
  
  void mKeyUp()
  {
   char[] checklist = {'-','.',' ',':','/','\\','_','0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
   boolean gotcha = false;
   //if(key != CODED) textEntry += key;
   for(int i = 0;i<checklist.length;i++)
      {if (key == checklist[i]) gotcha = true;}
   if(gotcha) textEntry += key;
 }
 
 
  void mRelease()
  {
    super.mRelease();
    if(mouseX > x+pg.width-53 && mouseX < x+pg.width-13 && mouseY > y+pg.height-38 && mouseY < y+pg.height-13 && !dragActive)
    {    
      mAccept();
    }else if(mouseX > x+pg.width-118 && mouseX < x+pg.width-58 && mouseY > y+pg.height-38 && mouseY < y+pg.height-13 && !dragActive)
    {
      mCancel();
    }
  }
  
  void mPress()
  {
    if(mouseX > x+82 && mouseX < x+142 && mouseY > y+62 && mouseY < y+87)
    {
      //do nothing...over the button
    }else if(mouseX > x+147 && mouseX < x+187 && mouseY > y+62 && mouseY < y+87){
      //do nothing...over the button
    }else if(mouseX > x && mouseX < x+pg.width && mouseY > y && mouseY < y+pg.height){
      dragActive = true;
    }
    if(RESIZEENABLED) checkResize();
  }
 
 void mAccept()
 {
   if(!DONE)
   {
     messages.add(new Message(textEntry,parent,target));     
   }
   DONE = true;
 }
 
 void mCancel()
 {
   if(!DONE)
   {
     textEntry = "";
     messages.add(new Message(textEntry,parent,target));
   }
   DONE = true;
 }
  
}

/*******************************
* The FilePop class starts in [PATH] and appends either \data or \projects based on whichDir.  
* This class sends a string message as a response.
*******************************/
//There's an inconsistency here with how clicks are handled versus the other two popups...
//in this one mouseDown activates buttons while in the ones above mouseUp activates buttons (to Do)
class FilePop extends Layer
{
  String    path = System.getProperty("user.dir");
  String[]  children;
  PGraphics fileList;
  PImage    selectBar;
  int       selected = -1;
  int       target;
  int       lastClick = 0;
  
  //slider variables
  boolean   sliderNeeded = false;
  boolean   sliderDrag = false;
  float     sliderMin = 20;  //20 + sliderHeight/2
  float     sliderMax = 160; //190 - sliderHeight/2
  float     sliderHeight = 30;
  float     sliderY = sliderMin;
  
  FilePop(int whichLayer, int whichCommand, int whichDir)
  {
    super(200,60,300,200,whichLayer);
    target = whichCommand;
    if(whichDir == 0) path += "\\data";
    if(whichDir == 1) path += "\\projects";
    
    selectBar = new PImage(160,20);
    for(int i = 0; i < selectBar.pixels.length; i++)
    {
      selectBar.pixels[i] = color(0,0,255,48);
    }
  
    populateFiles();
  }  
  
  void setPath(String s)
  {
    path = s;
  }
  
  void populateFiles()
  { 
    selected = -1;  //make sure no one is selected
    
    //create the file-list for the sliding window
    File dir = new File(path);   
    children = dir.list();
    fileList = createGraphics(160,max(168,(1+children.length)*20+6),JAVA2D);
    fileList.beginDraw();
    fileList.background(FILLCOLOR);
    fileList.fill(TEXTCOLOR);    
    fileList.textFont(font,16);    
    fileList.text("...",3,23);
    for(int i = 0; i < children.length; i++)
    {
      fileList.text(children[i],3,20*(i+2)+1);      
    }
    fileList.endDraw();
    if(fileList.height > 168) sliderNeeded = true;
    else sliderNeeded = false;
  }  
  
  boolean mDraw()
  {
    pg = createGraphics(w,h,JAVA2D);
    pg.beginDraw();
    pg.background(BORDERCOLOR);
    pg.fill(FILLCOLOR);
    pg.noStroke();
    pg.rect(3,3,pg.width-6,pg.height-6);

    //draw the slider if need-be
    if(sliderNeeded)
    {
      if(sliderDrag) sliderY = constrain(mouseY-y-sliderHeight/2,sliderMin,sliderMax);
      pg.strokeWeight(1);
      pg.stroke(BUTTONSTROKE);
      pg.fill(BUTTONSTROKE);
      pg.rect(10 + fileList.width+11,20,6,170);
      pg.fill(BUTTONFILL);
      pg.rect(10 + fileList.width+5,sliderY,18,sliderHeight);
    }
    
   float usable = sliderMax-sliderMin;
   float pct = (sliderY-sliderMin)/usable;
   float slideTotal = max(0,fileList.height-168);
   float slideAmt = slideTotal*pct;

    pg.noStroke();    
    pg.fill(BORDERCOLOR);
    pg.rect(10,20,fileList.width+4,170);
    pg.image(fileList,12,22-slideAmt);//22);
    pg.image(selectBar,12,26 + selected*20 - slideAmt);
    pg.rect(10,0,fileList.width+4,22);
    pg.rect(10,188,fileList.width+4,12);
    pg.fill(FILLCOLOR);
    pg.rect(10,3,fileList.width+4,17);
    pg.rect(10,190,fileList.width+4,7);
    
    pg.fill(TEXTCOLOR);    
    int fontSize = 20;
    pg.textFont(font,fontSize);
    while(pg.textWidth(path) > pg.width - 12 && fontSize > 6)
    {
      fontSize--;
      pg.textFont(font,fontSize);
    }
    pg.text(path,10,18);

    //basically, three buttons are needed:  (maybe later add more directory browsing like upDirectory and downDirectory)
    // -change directory
    // -accept selection
    // -cancel
    pg.strokeWeight(1);
    pg.stroke(BUTTONSTROKE);
    if(mouseX > x + 200 && mouseX < x + 290 && mouseY > y + 50 && mouseY < y + 90)
    {
      if(mousePressed) pg.fill(BUTTONDOWN);
      else pg.fill(BUTTONOVER);
    }else{
      pg.fill(BUTTONFILL); 
    }
    pg.rect(200,50,90,40);
    
    if(mouseX > x + 200 && mouseX < x + 290 && mouseY > y + 100 && mouseY < y + 140)
    {
      if(mousePressed) pg.fill(BUTTONDOWN);
      else pg.fill(BUTTONOVER);
    }else{
      pg.fill(BUTTONFILL); 
    }    
    pg.rect(200,100,90,40);
    
    if(mouseX > x + 200 && mouseX < x + 290 && mouseY > y + 150 && mouseY < y + 190)
    {
      if(mousePressed) pg.fill(BUTTONDOWN);
      else pg.fill(BUTTONOVER);
    }else{
      pg.fill(BUTTONFILL); 
    }    
    pg.rect(200,150,90,40);    
    
    pg.textAlign(CENTER);
    pg.fill(TEXTCOLOR);
    pg.textFont(font,24);
    pg.text("Dir",245,80);
    pg.text("Cancel",245,130);
    pg.text("Open",245,180); 
    
    pg.endDraw();
    
    doCursor();
    
    return DONE;
  }

  void mRelease()
  {
    super.mRelease();
    sliderDrag = false;
  }
  
  void mPress()
  {
    if(millis() - lastClick < DOUBLECLICK)
    {
      mAccept();
      return;
    }else{
      lastClick = millis();
    }
    if(mouseX > x + 10 && mouseY > y + 22 && mouseX < x+10+fileList.width && mouseY < y+22+fileList.height)
    {
      setSelected();
    }else if(sliderNeeded && mouseX > x + 10 + fileList.width + 5 && mouseX < x + 10 + fileList.width + 23 && mouseY > y + sliderY && mouseY < y + sliderY + sliderHeight){
      sliderDrag = true;      
    }else if(mouseX > x + 200 && mouseX < x + 290 && mouseY > y + 50 && mouseY < y + 90){
      //change directory
      layers.add(new PopText(layers.size()-1, 0, "Enter directory:", 0));
      ACTIVELAYER++;
    }else if(mouseX > x + 200 && mouseX < x + 290 && mouseY > y + 100 && mouseY < y + 140){
      mCancel();
    }else if(mouseX > x + 200 && mouseX < x + 290 && mouseY > y + 150 && mouseY < y + 190){
      mAccept();
    }else{
      dragActive = true;
    }
  }
  
  void mMessage(Message m)
  {
   if(m.type == 2 && !m.data.equals(""))
   {
     File dir = new File(m.data);  
     if(dir.isDirectory()){
       path = m.data; 
       populateFiles(); 
     }else{
       popWarn("Invalid directory!");
     }
   }  
  }
  
  void setSelected()
  {
     float usable = sliderMax-sliderMin;
     float pct = (sliderY-sliderMin)/usable;
     float slideTotal = max(0,fileList.height-168);
     float slideAmt = slideTotal*pct;
     
     selected = (int)(mouseY-y-22 + slideAmt)/20;
     if(selected > children.length) selected = -1;
  }
  
  void mAccept()
  {
    if(selected == -1) return;
    if(selected == 0)
    {
      File dir = new File(path);
      if(dir.getParent() != null)
      {
        path = dir.getParent();
        populateFiles();
      }
    }else{
      String filepath = path + "\\" + children[selected-1];
      File newfile = new File(filepath);
      if(newfile.isDirectory())
      {
        path = newfile.getPath();
        populateFiles();
      }else if(newfile.isFile()){
        messages.add(new Message(filepath,parent,target));     
        DONE = true;
      }else{
       popWarn("Invalid File");
      }
    }
  }
  
  void mCancel()
  {
    DONE = true;
  }
  
}

//messages are used to send information from one layer to the next
//each layer will have its own "mMessage" function with values for
//methods for decoding the messages and sending the appropriate calls
//All messages are sent as strings for simplicity's sake.
class Message
{
  int type;  //1 = boolean, 2 = string
  int parent;  //which layer is the recipient?
  int target;  //which function should be called?
  String data;
  
  Message(boolean b, int p, int t)
  {
    type = 1;
    data = "" + b;
    parent = p;
    target = t;
  }
  
  Message(String s, int p, int t)
  {
    type = 2;
    data = s;
    parent = p;
    target = t;
  }
}


/******************************
* LaunchButton
*******************************
* This class takes a String label, the Message it should pass on when clicked,
* its parent coordinates, its local coordinates, and a width/height.
******************************/
class LaunchButton
{
  int px,py,x,y,w,h;
  String label;
  Message m;

  
  LaunchButton(String l, Message _m, int _px, int _py, int _x, int _y, int _w, int _h)
  {
    label = l;
    m = _m;
    px = _px; py = _py;  //parent x,y
    x = _x; y = _y; 
    w = _w; h = _h;
  }
  
  void mDraw(PGraphics pg)
  {
    pg.stroke(BUTTONSTROKE);
    pg.strokeWeight(1);
    pg.fill(BUTTONFILL);
    if(mouseX > px + x  && mouseX < px + x + w && mouseY > py + y && mouseY < py + y + h)
    {
      pg.fill(BUTTONOVER);
      if(mousePressed) pg.fill(BUTTONDOWN);
    }
    pg.rect(x,y,w,h);
    pg.fill(TEXTCOLOR);
    pg.textAlign(LEFT);
    pg.textFont(font,12);
    float th = (h-12)/2;
    pg.text(label,x + 3, y + h - th);
  }
  
  boolean mTest()
  {
    return (mouseX > px + x  && mouseX < px + x + w && mouseY > py + y && mouseY < py + y + h);
  }
  
  void mRelease()
  {
    if(mTest())
    {
      println(messages.size());
      println(messages.size());      
      messages.add(m); 
    }
  }  
}
